using System;
using System.IO;
using System.Net;
using System.Net.Sockets ;
using System.Text.RegularExpressions;

namespace umlungu.engineX.pop3X {
	/// <summary>
	/// umlungu engineX blog Pop3 class.
	/// </summary>
	public class x_pop3
	{
		/// <summary>
		/// Local objects and properties
		/// </summary>
		public enum connect_state {disc,AUTHORIZATION,TRANSACTION,UPDATE};

		public string user;
		public string pwd;
		public string pop;
		public bool error;
		public connect_state state=connect_state.disc ;

		private TcpClient Server;
		private NetworkStream NetStrm;
		private StreamReader  RdStrm;
		private string Data;
		private byte[] szData;
		private string CRLF = "\r\n";	

		/// <summary>Constructors</summary>
		public x_pop3() {
			// just create to object	
		}
		public x_pop3(string pop_server,string user_name,string password) {
			pop=pop_server;
			user=user_name;
			pwd=password;
		}

		#region Public methods
		public string Connect (string pop_server) {
			pop=pop_server;
			return(connect());
		}
		public string Connect() {
			return(connect());
		}
		public void Login() { _USER(); _PASS(); }
		public void Logout() { _QUIT(); }
		public string List() { return(_LIST()); }
		public string List(int msg) { return(_LIST(msg)); }
		public string Retrieve(int msg) { return(_RETR(msg)); }
		#endregion
		
		#region Private methods
		private string connect (string pop_server) {
			pop=pop_server;
			return(connect());
		}
		private string connect() {
			Server = new TcpClient(pop,110);								
			try {
				NetStrm = Server.GetStream();
				RdStrm= new StreamReader(Server.GetStream());
				//The pop session is now in the AUTHORIZATION state
				state=connect_state.AUTHORIZATION ;
				return(RdStrm.ReadLine ());
			} catch(InvalidOperationException err) {
				return("Error: "+err.ToString());
			}

		}
		private string disconnect () {
			string temp="disconnected successfully.";
			if(state !=connect_state.disc) {

				//close connection
				NetStrm.Close();
				RdStrm.Close();
				state=connect_state.disc ;
			} else {
				temp="Not Connected.";
			}
			return(temp);
		}
		private void issue_command(string command) {
			Data= command + CRLF;
			szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
			NetStrm.Write(szData,0,szData.Length);

		}
		private string read_single_line_response() {
			string temp;
			try {
				temp = RdStrm.ReadLine();
				was_pop_error(temp);				
				return(temp);
			} catch(InvalidOperationException err) {
				return("Error in read_single_line_response(): " + err.ToString ()) ;
			}

		}
		private string read_multi_line_response() {
			string temp="";
			string szTemp;
			try {
				szTemp = RdStrm.ReadLine();
				was_pop_error(szTemp);				
				if(!error) {
					while(szTemp!=".") {
						temp += szTemp+CRLF;
						szTemp = RdStrm.ReadLine();
					}
				} else {
					temp=szTemp;
				}
				return(temp);
			} catch(InvalidOperationException err) {
				return("Error in read_multi_line_response(): " + err.ToString ());
			}
		}
		// detect if the pop server that issued the response believes that an error has occured.
		private void was_pop_error(string response) {
			if(response.StartsWith ("-")) {
				//if the first character of the response is "-" then the 
				//pop server has encountered an error executing the last 
				//command send by the client
				error=true;
			} else {
				error=false;
			}
		}
		#endregion
		
		#region Protected methods (POP commands)
		/// <summary>
		/// POP3 command: DELE
		/// <summary>
		protected string _DELE(int msg_number) {
			string temp;
			if (state != connect_state.TRANSACTION ) {
				//DELE is only valid when the pop session is in the TRANSACTION STATE
				temp="Connection state not = TRANSACTION";
			} else {
				issue_command("DELE " + msg_number.ToString ());
				temp=read_single_line_response();			
			}
			return(temp);
		}
		/// <summary>
		/// POP3 command: LIST
		/// <summary>
		protected string _LIST() {
			string temp="";
			if (state != connect_state.TRANSACTION ) {
				//the pop command LIST is only valid in the TRANSACTION state
				temp="Connection state not = TRANSACTION";
			} else {
				issue_command ("LIST");
				temp=read_multi_line_response();
			}
			return(temp);			
		}
		protected string _LIST(int msg_number) {
			string temp="";
			if (state != connect_state.TRANSACTION ) {
				//the pop command LIST is only valid in the TRANSACTION state
				temp="Connection state not = TRANSACTION";
			} else {
				issue_command ("LIST " + msg_number.ToString ());
				temp=read_single_line_response();  //when the message number is supplied, expect a single line response
			}
			return(temp);
		}
		/// <summary>
		/// POP3 command: NOOP
		/// <summary>
		protected string _NOOP() {
			string temp;
			if (state != connect_state.TRANSACTION ) {
				//the pop command NOOP is only valid in the TRANSACTION state
				temp="Connection state not = TRANSACTION";
			} else {
				issue_command ("NOOP");
				temp=read_single_line_response();
			}
			return(temp);
		}
		/// <summary>
		/// POP3 command: PASS
		/// <summary>
		protected string _PASS() {
			string temp;
			if (state != connect_state.AUTHORIZATION) {
				//the pop command PASS is only valid in the AUTHORIZATION state
				temp="Connection state not = AUTHORIZATION";
			} else {
				if (pwd !=null) {
					issue_command ("PASS " + pwd);
					temp=read_single_line_response();

					if (!error) {
						//transition to the Transaction state
						state=connect_state.TRANSACTION;
					}
				} else {
					temp="No Password set.";
				}
			}
			return(temp);
		}
		protected string _PASS(string password) {
			pwd=password;   //put the supplied password into the appropriate property
			return(_PASS()); //call PASS() with no arguement
		}
		/// <summary>
		/// POP3 command: QUIT
		/// <summary>
		protected string _QUIT() {
			//QUIT is valid in all pop states
			string temp;
			if (state !=connect_state.disc) {
				issue_command ("QUIT");
				temp=read_single_line_response();
				temp += CRLF + disconnect();
	
			} else {
				temp="Not Connected.";
			}
			return(temp);

		}
		/// <summary>
		/// POP3 command: RETR
		/// <summary>
		protected string _RETR (int msg) {
			string temp="";
			if (state != connect_state.TRANSACTION ) {
				//the pop command RETR is only valid in the TRANSACTION state
				temp="Connection state not = TRANSACTION";
			} else {
				// retrieve mail with number mail parameter
				issue_command ("RETR "+ msg.ToString ());
				temp=read_multi_line_response();
			}
			return(temp);

		}
		/// <summary>
		/// POP3 command: RSET
		/// <summary>
		protected string _RSET() {
			string temp;
			if (state != connect_state.TRANSACTION ) {
				//the pop command STAT is only valid in the TRANSACTION state
				temp="Connection state not = TRANSACTION";
			} else {
				issue_command("RSET");
				temp=read_single_line_response();
			}
			return(temp);

		}
		/// <summary>
		/// POP3 command: STAT
		/// <summary>
		protected string _STAT() {
			string temp;
			if (state==connect_state.TRANSACTION) {
				issue_command("STAT");
				temp=read_single_line_response();

				return(temp);
			} else {
				//the pop command STAT is only valid in the TRANSACTION state
				return ("Connection state not = TRANSACTION");
			}
		}		
		/// <summary>
		/// POP3 command: USER
		/// <summary>
		protected string _USER() {
			string temp;
			if (state != connect_state.AUTHORIZATION) {
				//the pop command USER is only valid in the AUTHORIZATION state
				temp="Connection state not = AUTHORIZATION";
			} else {
				if (user !=null) {   
					issue_command("USER "+ user);
					temp=read_single_line_response();
				} else {   //no user has been specified
					temp="No User specified.";
				}
			}
			return(temp);
		}
		protected string _USER(string user_name) {
			user=user_name;  //put the user name in the appropriate propertity
			return(_USER());  //call USER with no arguements
		}
		#endregion
	}

}
